package com.definity.toolkit.service;

import java.util.List;

import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.definity.toolkit.domain.Domain;
import com.definity.toolkit.persistence.Repository;
import com.definity.toolkit.persistence.RepositoryFactory;

public abstract class BasicDataService<D extends Domain<?>> implements DataService<D> {

	protected final Class<D> domainType;

	@Autowired
	private RepositoryFactory repositoryFactory;

	private Repository<D> repository;
	
	public BasicDataService(Class<D> type) {
		this.domainType = type;
	}

	public List<D> listAll() {
		return repository().listAll();
	}
	
	public D view(D domain) {
		if (domain == null || domain.getId() == null)
			return null;
		
		return repository().findById(domain.getId());
	}
	
	@Transactional
	public void delete(D domain) {
		D d = view(domain);
		if (d == null)
			throw new ServiceException("delete.domain.null");
		
		repository().delete(d);
	}
	
	@Transactional
	public D save(D domain) {
		if (domain == null)
			throw new ServiceException("save.domain.null");
		
		D existentDomain = view(domain);
		if (existentDomain != null)
			domain = merge(domain, existentDomain);
		
		return repository().save(domain);
	}
	
	@SuppressWarnings("unchecked")
	public List<D> find(String hql, Object... values) {
		Query query = repository().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i + 1, values[i]);
		}
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public D unique(String hql, Object... values) {
		Query query = repository().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i + 1, values[i]);
		}
		try {
			return (D) query.getSingleResult();
		} catch (NonUniqueResultException e) {
			return null;
		} catch (NoResultException e) {
			return null;
		}
	}

	protected D merge(D domain, D existentDomain) {
		return domain;
	}

	protected final Repository<D> repository() {
		if (repository == null)
			repository = createRepository(repositoryFactory);
		return repository;
	}

	protected Repository<D> createRepository(RepositoryFactory repositoryFactory) {
		return repositoryFactory.create(domainType);
	}
	
}
